<template>
    <div>
        <!-- 提问 -->
        <div v-if="!isAnswer" class="messages question">
            <div class="mr-2.5">
                <div class="text-xs text-right text-tx-placeholder">
                    <el-button v-if="isShowCopy" link @click="copy(content)">
                        <template #icon>
                            <icon name="el-icon-CopyDocument" />
                        </template>
                    </el-button>
                    <span class="ml-1">{{ createTime }}</span>
                </div>
                <div class="markdown-render">
                    <markdown :content="content" />
                    <div v-if="attach.length > 0" class="files">
                        <div v-for="(item, index) in attach" :key="index" class="file-box">
                            <el-image
                                v-if="item.type === 'file'"
                                :src="suffixUtil.findUnitIcon(item.file_name)"
                                :hide-on-click-modal="true"
                                class="w-[35px] h-[35px] rounded-md shrink-0"
                            />
                            <el-image
                                v-else
                                :src="item.file_path"
                                :preview-src-list="item.file_path ? [item.file_path] : []"
                                :hide-on-click-modal="true"
                                class="w-[35px] h-[35px] rounded-md shrink-0"
                            />
                            <div>
                                <div class="text-tx-regular mb-1 line-clamp-1">
                                    {{ item.file_name }}
                                </div>
                                <div class="text-tx-secondary line-clamp-1">
                                    {{ item.file_ext?.toUpperCase() }}
                                    {{ toolsUtil.formatBytes(item.file_size) }}
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <el-avatar :src="avatar" />
        </div>

        <!-- 回复 -->
        <div v-else class="messages answer">
            <el-avatar
                :src="avatar"
                class="w-[35px] h-[35px] rounded-md shrink-0"
            />
            <div class="relative ml-2.5 overflow-auto">
                <div class="text-xs text-tx-placeholder">
                    <span>{{ createTime }}</span>
                    <el-tag
                        v-if="chatModel && isShowChatModel"
                        type="success"
                        class="ml-2"
                    >
                        {{ chatModel }}
                    </el-tag>
                </div>
                <div class="markdown-render">
                    <div v-if="webSearch === null" class="web-search">
                        <icon name="el-icon-Loading" :size="16" class="mr-1 is-loading" />
                        <span>正在联网搜索</span>
                    </div>

                    <div
                        v-else-if="webSearch.length > 0"
                        class="web-search"
                        @click="emits('search', webSearch)"
                    >
                        <icon name="el-icon-Search" />
                        <span>已搜索到 {{ webSearch.length  }} 个网页</span>
                    </div>

                    <el-collapse v-for="(tool, ti) in mcpToolsList" :key="ti" @change="mcpClick(ti)">
                        <el-collapse-item :name="ti">
                            <template #title>
                                <photos
                                    v-if="tool.response"
                                    :src="tool.tool_logo"
                                    width="18px"
                                    height="18px"
                                    radius="4px"
                                    class="!grid mr-2"
                                />
                                <icon v-else name="el-icon-Loading" :size="16" class="mr-2 is-loading" />
                                <span>{{ tool.name }}</span>
                            </template>
                            <template v-if="mcpShowArray.length > 0 && mcpShowArray.includes(ti)">
                                <markdown
                                    :content="'```Arguments\n'+ tool.arguments +'\n```'"
                                    :lineNumbers="false"
                                    class="mb-3"
                                />
                                <markdown
                                    :content="'```Response\n'+ tool.response +'\n```'"
                                    :lineNumbers="false"
                                    class="mb-3"
                                />
                            </template>
                        </el-collapse-item>
                    </el-collapse>

                    <el-collapse v-if="thinks" model-value="think-reasoning">
                        <el-collapse-item title="已深度思考" name="think-reasoning">
                            <markdown
                                :content="thinks"
                                :typing="loading"
                                class="!text-tx-placeholder"
                            />
                        </el-collapse-item>
                    </el-collapse>

                    <markdown v-if="content" :content="content" :typing="loading" />

                    <div v-if="annexImages.length > 0"  class="flex flex-wrap gap-2 mt-2">
                        <el-image
                            v-for="(ax, i) in annexImages"
                            :key="i"
                            :preview-src-list="annexImages"
                            :preview-teleported="true"
                            :hide-on-click-modal="true"
                            :src="ax.file_path"
                            class="w-[100px] h-[100px] rounded-md"
                        />
                    </div>

                    <template v-for="ax in (annex as ChatRecordsAnnex[])" :key="ax.file_path">
                        <div v-if="ax.file_type !== 'image'" class="flex flex-col mt-2">
                            <nuxt-link
                                class="flex items-center border rounded-md bg-lighter p-2 cursor-pointer"
                                target="_blank"
                                :external="true"
                                :to="ax.file_path"
                            >
                                <el-image
                                    class="w-[18px] h-[18px] mr-2"
                                    :src="suffixUtil.findUnitIcon(ax.file_name)"
                                />
                                <span class="text-base underline hover:text-tx-regular">
                                    {{ ax.file_name }}
                                </span>
                            </nuxt-link>
                        </div>
                    </template>

                    <div v-if="loading && !content && !thinks" class="py-2">
                        <div class="flashing"></div>
                    </div>

                    <div v-if="!loading" class="mt-2">
                        <el-tag
                            v-if="isShowQuote && quotes.length > 0"
                            type="primary"
                            effect="plain"
                            class="mr-2 cursor-pointer"
                            @click="popupQuotes = true"
                        >
                            {{ quotes.length }}条引用
                        </el-tag>
                        <el-tag
                            v-if="isShowContext && context.length > 0"
                            type="success"
                            effect="plain"
                            class="mr-2 cursor-pointer"
                            @click="popupContext = true"
                        >
                            {{ context.length }}条上下文
                        </el-tag>
                        <el-tag
                            v-if="isShowTimed && taskTime"
                            type="info"
                            effect="plain"
                            class="mr-2 cursor-pointer"
                        >
                            {{ taskTime }}
                        </el-tag>
                    </div>
                </div>

                <div v-if="!loading" class="mt-[5px]">
                    <el-button v-if="isShowRewrite" link @click="emits('rewrite')">
                        <template #icon>
                            <icon name="el-icon-RefreshLeft" />
                        </template>
                        <span class="font-normal">重新回答</span>
                    </el-button>
                    <el-button v-if="isShowCopy" link @click="copy(content)">
                        <template #icon>
                            <icon name="el-icon-CopyDocument" />
                        </template>
                        <span class="font-normal">复制</span>
                    </el-button>
                    <template v-if="isShowVoice">
                        <el-button v-if="audioPlaying" link @click="speechPause">
                            <template #icon>
                                <icon name="svg-icon-Voice" />
                            </template>
                            <span class="font-normal">停止</span>
                        </el-button>
                        <el-button v-else link :loading="audioLoading" @click="speechStart">
                            <template #icon>
                                <icon name="svg-icon-Voice" />
                            </template>
                            <span class="font-normal">朗读</span>
                        </el-button>
                    </template>
                    <!-- <el-button v-if="isShowShare" link>
                        <template #icon>
                            <icon name="svg-icon-Share" />
                        </template>
                        <span class="font-normal">分享</span>
                    </el-button> -->
                </div>

                <el-button
                    v-if="loading && isShowStop"
                    type="primary"
                    size="small"
                    class="mx-2 mt-2"
                    link
                    @click="emits('stop')"
                >
                    <icon name="el-icon-Refresh" class="is-loading" />
                    <span>停止生成</span>
                </el-button>

                <div v-if="isShowIssues && issues.length > 0" class="mt-3">
                    <span class="text-sm text-tx-placeholder">你可以继续问我：</span>
                    <div class="question-guide">
                        <div
                            v-for="(item, index) in issues"
                            :key="index"
                            class="ask"
                            @click="emits('ask', item)"
                        >{{ item }}</div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 清除线 -->
        <div
            v-if="isContextClear"
            class="flex justify-center"
        >
            <el-divider
                content-position="center"
                class="!max-w-[600px]"
            >
                <span class="text-tx-placeholder">上下文已清除</span>
            </el-divider>
        </div>

        <!-- 弹窗: 上下文 -->
        <chatting-popup-context
            :show="popupContext"
            :context="context"
            @close="popupContext = false"
        />

        <!-- 弹窗:  -->
        <chatting-popup-quotes
            :show="popupQuotes"
            :quotes="quotes"
            @close="popupQuotes = false"
        />
    </div>
</template>

<script setup lang="ts">
import voiceApi from '~/api/voice/index'
import toolsUtil from '~/utils/tools'
import suffixUtil from '~/utils/suffix'

const props = withDefaults(
    defineProps<{
        // 类型
        type: string;
        // 消息ID
        recordId: number | string;
        // 头像
        avatar: string;
        // 内容
        content: string;
        // 思考
        thinks?: string;
        // 上下文
        context?: any[];
        // 引用内容
        quotes?: any[];
        // 提问附件
        attach?: any[];
        // 答复附件
        annex?: any[];
        // 相关问题
        issues?: any[];
        // 联网搜索
        webSearch?: any[] | null;
        // MCP工具
        toolsList?: any[];
        // 执行耗时
        taskTime?: string;
        // 创建时间
        createTime?: string;
        // 对话模型名称
        chatModel?: string;
        // 显示停止按钮
        isShowStop?: boolean;
        // 显示复制按钮
        isShowCopy?: boolean;
        // 显示朗读按钮
        isShowVoice?: boolean;
        // 显示分享按钮
        isShowShare?: boolean;
        // 显示重新回答
        isShowRewrite?: boolean;
        // 显示耗时时间
        isShowTimed?: boolean;
        // 显示上下文组
        isShowContext?: boolean;
        // 显示引用内容
        isShowQuote?: boolean;
        // 显示相关问题
        isShowIssues?: boolean;
        // 显示对话模型
        isShowChatModel?: boolean;
        // 显示清除线条
        isContextClear?: boolean;
        // 回复中的状态
        loading?: boolean;
        // 语音播报参数
        broadcast?: any;
    }>(),
    {
        thinks: '',
        context: () => [],
        quotes: () => [],
        attach: () => [],
        annex: () => [],
        issues: () => [],
        webSearch: () => [],
        toolsList: () => [],
        taskTime: '',
        createTime: '',
        chatModel: '',
        isShowStop: true,
        isShowCopy: false,
        isShowVoice: false,
        isShowShare: false,
        isShowRewrite: false,
        isShowContext: false,
        isShowQuote: false,
        isShowIssues: false,
        isContextClear: false,
        isShowChatModel: false,
        loading: false,
        broadcast: {
            model: 'browser:utterance',
            speed: 1
        }
    }
)

const emits = defineEmits<{
    (event: 'rewrite' | 'stop' | 'ask'): void
    (event: 'search', lists: any[]): void
    (event: 'ask', question: string): void
}>()

const { copy } = useCopy()
const { audioLoading, audioPlaying, play, pause } = useAudioPlay()

// 引用组弹窗
const popupQuotes = ref<boolean>(false)
// 上下文弹窗
const popupContext = ref<boolean>(false)
// 是否是答复
const isAnswer = computed(() => props.type === 'answer')

// 答复的图片
const annexImages = computed(() => {
    return props.annex
        .filter(file => file.file_type === 'image')
        .map(file => file.file_path)
})

// MCP工具
const mcpToolsList = computed(() => {
    return props.toolsList.reduce((acc, current) => {
        const existing = acc.find((item: any) => item.id === current.id)
        if (current.arguments) {
            try {
                const args = JSON.parse(current.arguments)
                current.arguments = JSON.stringify(args, null, 4)
            } catch {}
        }
        if (current.response) {
            try {
                const resp = JSON.parse(current.response)
                current.response = JSON.stringify(resp, null, 4)
            } catch {}
        }
        if (existing) {
            Object.keys(current).forEach(key => {
                if (current[key] !== '' && current[key] !== null) {
                    existing[key] = current[key]
                }
            })
        } else {
            acc.push(current)
        }
        return acc
    }, [])
})

// MCP展开
const mcpShowArray = ref<number[]>([])
const mcpClick = (index: number) => {
    if (!mcpShowArray.value.includes(index)) {
        mcpShowArray.value.push(index)
    }
}

/**
 * 浏览器语音播放
 *
 * @author zero
 */
let utterance: any = null
const speechStart = async () => {
    const channel = props.broadcast.model.split(':')[0]
    if (channel === 'browser') {
        const speed: number = props.broadcast.speed || 1
        if (window.speechSynthesis && window.speechSynthesis.speak) {
            utterance = new SpeechSynthesisUtterance(props.content)
            utterance.lang = 'zh-CN'
            utterance.rate = speed // 语速: [0.1 ~ 10]
            utterance.pitch = 1    // 音调: [0 ~ 2]
            utterance.volume = 1   // 音量: [0 ~ 1]
            window.speechSynthesis.speak(utterance)
            utterance.onstart = () => {
                audioPlaying.value = true
            }
            utterance.onend = () => {
                utterance = null
                audioPlaying.value = false
            }
            utterance.onerror = (event: any) => {
                utterance = null
                audioPlaying.value = false
                if (event.error !== 'interrupted') {
                    feedback.msgError('朗读出错: ' + event.error)
                }
            }
        } else {
            feedback.msgError('当前浏览器不支持语音合成')
        }
    } else {
        const rid: number = parseInt(String(props.recordId))
        const data = await voiceApi.speech({rid: rid})
        await play(data.url)
    }
}

/**
 * 浏览器语音结束
 *
 * @author zero
 */
const speechPause = () => {
    const channel = props.broadcast.model.split(':')[0]
    if (channel === 'browser') {
        if (utterance && window.speechSynthesis.cancel) {
            audioPlaying.value = false
            window.speechSynthesis.cancel()
        }
    } else {
        pause()
    }
}
</script>

<style scoped lang="scss">
.messages {
    display: flex;
    margin-bottom: 25px;
    .markdown-render {
        position: relative;
        box-sizing: border-box;
        min-width: 130px;
        max-width: 900px;
        padding: 14px;
        margin-top: 8px;
        border-radius: 8px;
    }
    &:first-child {
        margin-top: 25px;
    }
    &.answer {
        justify-content: flex-start;
        .markdown-render {
            background-color: var(--el-bg-color-page);
        }
    }
    &.question {
        justify-content: flex-end;
        .markdown-render {
            color: var(--el-text-color-regular);
            background-color: var(--el-color-primary-light-9);
        }
    }
    .files {
        display: flex;
        flex-wrap: wrap;
        gap: 8px;
        align-items: center;
        align-self: flex-start;
        width: 100%;
        margin-top: 10px;
        margin-bottom: 10px;
        .file-box {
            position: relative;
            box-sizing: border-box;
            display: flex;
            gap: 10px;
            align-items: center;
            width: 190px;
            height: 52px;
            padding: 10px;
            font-size: 12px;
            background-color: var(--color-white);
            border-radius: 8px;
            .close {
                position: absolute;
                top: -5px;
                right: -5px;
                display: flex;
                align-items: center;
                justify-content: center;
                color: var(--el-text-color-regular);
                &:hover {
                    color: var(--el-color-black);
                    cursor: pointer;
                }
            }
        }
    }
}

.flashing {
    position: relative;
    width: 10px;
    height: 10px;
    margin-left: 20px;
    color: var(--el-color-primary);
    background-color: var(--el-color-primary);
    border-radius: 5px;
    animation: dotFlashing 1s infinite linear alternate;
    animation-delay: .5s;
    &::before, &::after {
        position: absolute;
        top: 0;
        display: inline-block;
        content: '';
    }
    &::before {
        left: -15px;
        width: 10px;
        height: 10px;
        color: var(--el-color-primary);
        background-color: var(--el-color-primary);
        border-radius: 5px;
        animation: dotFlashing 1s infinite alternate;
        animation-delay: 0s;
    }
    &::after {
        left: 15px;
        width: 10px;
        height: 10px;
        color: var(--el-color-primary);
        background-color: var(--el-color-primary);
        border-radius: 5px;
        animation: dotFlashing 1s infinite alternate;
        animation-delay: 1s;
    }

    @keyframes dotFlashing {
        0% { background-color: var(--el-color-primary); }
        50%, 100% { background-color: var(--el-color-primary-light-8); }
    }
}

.question-guide {
    margin-top: 10px;
    .ask {
        width: fit-content;
        padding: 8px 15px;
        margin-top: 10px;
        font-size: 13px;
        color: var(--el-text-color-placeholder);
        cursor: pointer;
        background-color: var(--el-bg-color-lighter);
        border-radius: 8px;
        &:hover {
            color: var(--el-color-primary);
            background-color: var(--el-color-primary-light-9);
        }
    }
}

.web-search {
    display: flex;
    gap: 5px;
    align-items: center;
    width: fit-content;
    padding: 8px 14px;
    margin-bottom: 10px;
    font-size: 12px;
    line-height: 18px;
    cursor: pointer;
    user-select: none;
    background: var(--el-bg-color);
    border-radius: 6px;
}

:deep(.el-collapse) {
    margin-bottom: 15px;
    border: none;
    .el-collapse-item__header {
        width: fit-content;
        height: 34px;
        padding: 0 10px;
        margin-bottom: 10px;
        font-weight: normal;
        border-radius: 8px;
        .el-collapse-item__arrow {
            margin-left: 10px;
        }
    }

    .el-collapse-item__wrap {
        padding: 0 10px;
        font-size: var(--el-font-size-small);
        color: var(--el-text-color-secondary);
        background-color: unset;
        border-bottom: none;
        border-left: 3px solid var(--el-border-color);
    }

    .el-collapse-item__title {
        display: flex;
        align-items: center;
        height: inherit;
    }

    .el-collapse-item__content {
        padding: 0;
    }
}
</style>
